#include "widget.h"
#include "widget_ind.h"
#include "widget_arg.h"

//=====================================================================
//                    Widget - analog of PtWidget
//=====================================================================
static PyMemberDef Widget_members [] = 
{
  {NULL}  // Sentinel 
};

static PyMethodDef Widget_methods [] =
{
  {
    "create", (PyCFunction) Widget_create, METH_NOARGS,
    "Analog of PtCreateWidget"
  },

  {
    "set_callback", (PyCFunction) Widget_set_callback, METH_VARARGS,
    "Analog of PtSetCallback"
  },

  {
    "get_arg", (PyCFunction) Widget_get_arg, METH_VARARGS,
    "Analog of PtSetResources"
  },

  {
    "set_arg", (PyCFunction) Widget_set_arg, METH_VARARGS,
    "Analog of PtSetResources"
  },

  {
    "realize", (PyCFunction) Widget_realize, METH_NOARGS,
    "Analog of PtRealizeWidget"
  },

  {
    "set_default_parent", (PyCFunction) Widget_set_default_parent, 
    METH_NOARGS, "Setting Pt_DEFAULT_PARENT"
  },

  {
    "set_no_parent", (PyCFunction) Widget_set_no_parent, METH_NOARGS,
    "Setting Pt_NO_PARENT"
  },

  {
    "set_parent", (PyCFunction) Widget_set_parent, METH_VARARGS,
    "Setting parent"
  },

  {NULL}  // Sentinel 
};

static PyTypeObject WidgetType = 
{
  PyObject_HEAD_INIT(NULL)
  0,                         // ob_size 
  "ph.Widget",               // tp_name
  sizeof(Widget),            // tp_basicsize 
  0,                         // tp_itemsize
  (destructor)Widget_dealloc,// tp_dealloc
  0,                         // tp_print 
  0,                         // tp_getattr 
  0,                         // tp_setattr
  0,                         // tp_compare 
  0,                         // tp_repr
  0,                         // tp_as_number
  0,                         // tp_as_sequence 
  0,                         // tp_as_mapping 
  0,                         // tp_hash 
  0,                         // tp_call
  0,                         // tp_str
  0,                         // tp_getattro
  0,                         // tp_setattro
  0,                         // tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags 
  "Photon wrapper classes",  // tp_doc 
  0,                         // tp_traverse 
  0,                         // tp_clear 
  0,                         // tp_richcompare 
  0,                         // tp_weaklistoffset 
  0,                         // tp_iter 
  0,                         // tp_iternext 
  Widget_methods,            // tp_methods 
  Widget_members,            // tp_members
  0,                         // tp_getset 
  0,                         // tp_base 
  0,                         // tp_dict
  0,                         // tp_descr_get 
  0,                         // tp_descr_set 
  0,                         // tp_dictoffset 
  (initproc)Widget_init,     // tp_init
  0,                         // tp_alloc 
  Widget_new,                // tp_new 
};

static void Widget_dealloc (Widget* self)
{
  self->ob_type->tp_free ((PyObject*) self);
}

static PyObject* Widget_new (PyTypeObject* type, PyObject* args, PyObject* kwds)
{
  Widget* self;

  self = (Widget*) type->tp_alloc (type, 0);
  return (PyObject*) self; 
}

static int Widget_init (Widget* self, PyObject* args)
{
  WidgetInd* widget_ind;

#if DEBUG_LEVEL == HIGH
  MARK ("Initializing widget");
#endif

  if (PyArg_ParseTuple (args, "O", &widget_ind))
  {
    self->widget_class = widget_ind->widget_class;
    self->parent = Pt_DEFAULT_PARENT;
    self->widget = NULL;
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, "set_ind accepts only WidgetInd");
    return -1;
  }
}

/**
 * w.create ()
 */
static PyObject* Widget_create (Widget* self)
{
  PyObject*   result;
  PtWidget_t* widget;

#if 0
  if (!g_ph_initialized)
  {
    PyErr_SetString (PyExc_RuntimeError, 
        "No connection to Photon library");
    return NULL;
  }
#endif

  if ((widget = PtCreateWidget(self->widget_class, self->parent, 
          0, NULL)) == NULL)
  {
    PyErr_SetString (PyExc_RuntimeError, 
        "Unable to create widget");
    return NULL;
  }

  self->widget = widget;

#if DEBUG_LEVEL == HIGH
  REPORT ("%x", self->widget);
  REPORT ("%x", self->parent);
#endif

  Py_INCREF (Py_None);
  result = Py_None;

  return result;
}

/**
 * w.realize()
 */
static PyObject* Widget_realize (Widget* self)
{
  PyObject* result;

  if (!self->widget)
  {
    PyErr_SetString (PyExc_RuntimeError, 
        "Widget not created");
    return NULL;
  }

  PtRealizeWidget (self->widget);

  Py_INCREF (Py_None);
  result = Py_None;

  return result;
}

/**
 * w.set_default_parent()
 */
static PyObject* Widget_set_default_parent (Widget* self)
{
  self->parent = Pt_DEFAULT_PARENT;
}

/**
 * w.set_no_parent()
 */
static PyObject* Widget_set_no_parent (Widget* self)
{
  self->parent = Pt_NO_PARENT;
}

/**
 * w.set_parent(parent)
 */
static PyObject* Widget_set_parent (Widget* self, PyObject* args)
{
  Widget *parent;
  PyObject *result;

  if (PyArg_ParseTuple (args, "O", &parent))
  {

#if DEBUG_LEVEL == HIGH
    MARK ("Setting parent widget");
    REPORT ("%x", parent->widget);
    REPORT ("%x", self);
#endif

    self->parent = parent->widget;
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "Incorrect type passed to become widget parent");
    return NULL;
  }

  Py_INCREF (Py_None);
  result = Py_None;

  return result;
}

typedef enum 
{
  eCallPtSet,
  eCallPtGet
} ResourceAccessType;

static PyObject* Widget_access_arg (Widget* self, PyObject* args, ResourceAccessType access_type)
{
  WidgetArg *w_arg;
  PyObject  *result;
  int pt_call_result = 0;

  if (self->widget == NULL)
  {
    PyErr_SetString (PyExc_RuntimeError, 
        "Cannot set resources until widget created");
    return NULL;
  }

  if (PyArg_ParseTuple (args, "O", &w_arg))
  {

#if DEBUG_LEVEL == HIGH
    REPORT ("%d", w_arg->num);
    REPORT ("%x", w_arg->arg);
#endif

    switch (access_type)
    {
      case eCallPtSet:
        pt_call_result = PtSetResources (self->widget, 
            w_arg->num, w_arg->arg);
        break;
      case eCallPtGet:
        pt_call_result = PtGetResources (self->widget, 
            w_arg->num, w_arg->arg);
        break;
    }

#if DEBUG_LEVEL == HIGH
    REPORT ("%d", pt_call_result);
#endif

    if (pt_call_result == -1)
    {
      PyErr_SetString (PyExc_RuntimeError, 
          "Resources cannot be set by Photon library");
      return NULL;
    }
  }
  else
  {
    PyErr_SetString (PyExc_RuntimeError, 
        "Set resources must be passed Widget_arg object only");
    return NULL;
  }

  Py_INCREF (Py_None);
  result = Py_None;

  return result;
}

/**
 * w.set_arg (ph.Widget_arg ([("text", "hello")])
 */
static PyObject* Widget_set_arg (Widget* self, PyObject* args)
{
  return Widget_access_arg (self, args, eCallPtSet);
}

static PyObject* Widget_get_arg (Widget* self, PyObject* args)
{
  return Widget_access_arg (self, args, eCallPtGet);
}

static int sender_callback (PtWidget_t *w, void *data, 
    PtCallbackInfo_t *cbinfo)
{
  WidgetCallbackData *widget_callback_data = (WidgetCallbackData*) data;
  PyObject *arglist;

  if (widget_callback_data)
  {
    PyObject_CallObject (widget_callback_data->callable, 
        widget_callback_data->args);
    // free (widget_callback_data);
  }

  return( Pt_CONTINUE );
}

static int post_menu_callback (PtWidget_t *w, void *client_data, 
    PtCallbackInfo_t *info)
{
   client_data = client_data; info = info;

   if (client_data)
   {
      PtWidget_t *menu = (PtWidget_t *)client_data;

      PtPositionMenu(menu, NULL);
      PtRealizeWidget(menu);
   }
   return (Pt_CONTINUE);
}

static int set_post_menu_callback (Widget* self, PyObject *menu_object)
{
  Widget* menu_widget = (Widget*) menu_object;

  if (!menu_widget->widget)
  {
    PyErr_SetString (PyExc_TypeError, 
        "post_menu widget must be initialized");
    return -1;
  }

#if DEBUG_LEVEL == HIGH
  MARK ("Setting post_menu callback");
#endif


  PtAddCallback (self->widget, Pt_CB_ARM, post_menu_callback, menu_widget->widget);
}

static int set_sender_callback (Widget* self, PyObject* callback_object)
{
  PtCallback_t          callback;
  WidgetCallbackData   *widget_callback_data;

  if (!PyCallable_Check (callback_object))
  {
    PyErr_SetString (PyExc_TypeError, 
        "callback parameter must be callable");
    return -1;
  } 

  Py_XINCREF (callback_object);

  widget_callback_data = malloc (sizeof (WidgetCallbackData));
  widget_callback_data->callable = callback_object;

  widget_callback_data->args = Py_BuildValue ("(N)", self);

  callback.event_f = sender_callback;
  callback.data = widget_callback_data;

#if DEBUG_LEVEL == HIGH
  MARK ("Setting sender callback");
#endif

  PtSetResource (self->widget, Pt_CB_ACTIVATE, &callback, 1);

  return 0;
}

static int set_none_callback (Widget* self, PyObject* none)
{
  return -1;
}

static CallbackType find_callback_type (const char* repr)
{
  if (!strcmp (repr, "sender"))
    return eSender;

  if (!strcmp (repr, "post_menu"))
    return ePostMenu;

  return eNoneCallback;
}

static int (*g_set_callback_table []) (Widget*, PyObject*) =
{
  set_none_callback,
  set_sender_callback,
  set_post_menu_callback
};

/**
 * @brief w.set_callback ("type", py_object)
 */
static PyObject* Widget_set_callback (Widget* self, PyObject* args)
{
  PyObject             *value, 
                       *result;
  const char           *ind;

#if DEBUG_LEVEL == HIGH
      MARK ("Setting callback")
#endif

  if (PyArg_ParseTuple (args, "sO", &ind, &value))
  {
    if (g_set_callback_table [find_callback_type (ind)] 
        (self, value) == -1)
      return NULL;
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "set_callback must be passed with string key and object");
    return NULL;
  }

  Py_INCREF (Py_None);
  result = Py_None;

  return result;
}

//=====================================================================
//                Module declaration functions
//=====================================================================

static PyMethodDef module_methods [] = 
{
  {NULL, NULL, 0, NULL}
};

INIT_FUNC (ph_api_widget, Widget)
